//@ edition: 2021
//@ run-rustfix
//@ rustfix-only-machine-applicable
//@ aux-build:migration_lint_macros.rs
#![feature(mut_ref)]
#![allow(incomplete_features, unused)]
#![deny(rust_2024_incompatible_pat)]

extern crate migration_lint_macros;

struct Foo<T>(T);

// Tests type equality in a way that avoids coercing `&&T` to `&T`.
trait Eq<T> {}
impl<T> Eq<T> for T {}
fn assert_type_eq<T, U: Eq<T>>(_: T, _: U) {}

fn main() {
    let Foo(x) = &Foo(0);
    assert_type_eq(x, &0u8);

    let Foo(x) = &mut Foo(0);
    assert_type_eq(x, &mut 0u8);

    let Foo(mut x) = &Foo(0);
    //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(x, 0u8);

    let Foo(mut x) = &mut Foo(0);
    //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(x, 0u8);

    let Foo(ref x) = &Foo(0);
    //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(x, &0u8);

    let Foo(ref x) = &mut Foo(0);
    //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(x, &0u8);

    let &Foo(x) = &Foo(0);
    assert_type_eq(x, 0u8);

    let &mut Foo(x) = &mut Foo(0);
    assert_type_eq(x, 0u8);

    let &Foo(x) = &Foo(&0);
    assert_type_eq(x, &0u8);

    let &mut Foo(x) = &mut Foo(&0);
    assert_type_eq(x, &0u8);

    let Foo(&x) = &Foo(&0);
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(x, 0u8);

    let Foo(&mut x) = &Foo(&mut 0);
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(x, 0u8);

    let Foo(&x) = &mut Foo(&0);
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(x, 0u8);

    let Foo(&mut x) = &mut Foo(&mut 0);
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(x, 0u8);

    if let Some(x) = &&&&&Some(&0u8) {
        assert_type_eq(x, &&0u8);
    }

    if let Some(&x) = &&&&&Some(&0u8) {
        //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
        //~| WARN: this changes meaning in Rust 2024
        assert_type_eq(x, 0u8);
    }

    if let Some(&mut x) = &&&&&Some(&mut 0u8) {
        //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
        //~| WARN: this changes meaning in Rust 2024
        assert_type_eq(x, 0u8);
    }

    if let Some(&x) = &&&&&mut Some(&0u8) {
        //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
        //~| WARN: this changes meaning in Rust 2024
        assert_type_eq(x, 0u8);
    }

    if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) {
        //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
        //~| WARN: this changes meaning in Rust 2024
        assert_type_eq(x, &mut 0u8);
    }

    struct Struct<A, B, C> {
        a: A,
        b: B,
        c: C,
    }

    let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 };
    //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &0u32);
    assert_type_eq(b, 0u32);

    let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 };
    //~^ ERROR: cannot explicitly borrow or dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, 0u32);
    assert_type_eq(b, &&0u32);
    assert_type_eq(c, &&0u32);

    if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } =
        //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
        //~| WARN: this changes meaning in Rust 2024
        &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) })
    {
        assert_type_eq(a, &0u32);
        assert_type_eq(b, 0u32);
        assert_type_eq(c, &&0u32);
    }

    match &(Some(0), Some(0)) {
        // The two patterns are the same syntactically, but because they're defined in different
        // editions they don't mean the same thing.
        (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => {
            //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern
            assert_type_eq(x, 0u32);
            assert_type_eq(y, 0u32);
        }
        _ => {}
    }

    let [&mut [ref a]] = &mut [&mut &[0]];
    //~^ ERROR: cannot explicitly borrow or dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &0u32);

    let [&(_)] = &[&0];
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024

    // NB: Most of the following tests are for possible future improvements to migration suggestions

    // Test removing multiple binding modifiers.
    let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 };
    //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &0u32);
    assert_type_eq(c, &0u32);

    // Test that we don't change bindings' modes when removing binding modifiers.
    let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 };
    //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &0u32);
    assert_type_eq(b, &mut 0u32);
    assert_type_eq(c, &mut 0u32);

    // Test removing multiple reference patterns of various mutabilities, plus a binding modifier.
    let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 };
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &0u32);
    assert_type_eq(b, &0u32);
    assert_type_eq(c, &0u32);

    // Test that we don't change bindings' types when removing reference patterns.
    let Foo(&ref a) = &Foo(&0);
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &0u32);

    // Test that we don't change bindings' modes when adding reference paterns (caught early).
    let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]);
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, 0u32);
    assert_type_eq(b, &0u32);
    assert_type_eq(c, &0u32);
    assert_type_eq(d, &0u32);
    assert_type_eq(e, &0u32);

    // Test that we don't change bindings' modes when adding reference patterns (caught late).
    let (a, [b], [mut c]) = &(0, &mut [0], &[0]);
    //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &0u32);
    assert_type_eq(b, &0u32);
    assert_type_eq(c, 0u32);

    // Test featuring both additions and removals.
    let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0]));
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, 0u32);
    assert_type_eq(b, &0u32);
    assert_type_eq(c, &0u32);

    // Test that bindings' subpatterns' modes are updated properly.
    let [mut a @ b] = &[0];
    //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, 0u32);
    assert_type_eq(b, &0u32);

    // Test that bindings' subpatterns' modes are checked properly.
    let [a @ mut b] = &[0];
    //~^ ERROR: cannot mutably bind by value within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &0u32);
    assert_type_eq(b, 0u32);

    // Test that we respect bindings' subpatterns' types when rewriting `&ref x` to `x`.
    let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2];
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &0u32);
    assert_type_eq(b, &0u32);
    assert_type_eq(c, &0u32);
    assert_type_eq(d, 0u32);

    // Test that we respect bindings' subpatterns' modes when rewriting `&ref x` to `x`.
    let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2];
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &[0u32]);
    assert_type_eq(b, &0u32);
    assert_type_eq(c, &[0u32]);
    assert_type_eq(d, 0u32);

    // Test that we use the correct message and suggestion style when pointing inside expansions.
    let [migration_lint_macros::bind_ref!(a)] = &[0];
    //~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern
    assert_type_eq(a, &0u32);

    // Test that we use the correct span when labeling a `&` whose subpattern is from an expansion.
    let [&migration_lint_macros::bind_ref!(a)] = &[&0];
    //~^ ERROR: cannot explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, &0u32);

    // Test the primary diagnostic message for mixes of `mut`/`ref`/`&`.
    let (mut a, ref b) = &(0, 0);
    //~^ ERROR: cannot write explicit binding modifiers within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, 0u32);
    assert_type_eq(b, &0u32);

    let (mut a, &b) = &(0, &0);
    //~^ ERROR: cannot mutably bind by value or explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, 0u32);
    assert_type_eq(b, 0u32);

    let (mut a, ref b, &c) = &(0, 0, &0);
    //~^ ERROR: cannot write explicit binding modifiers or explicitly dereference within an implicitly-borrowing pattern in Rust 2024
    //~| WARN: this changes meaning in Rust 2024
    assert_type_eq(a, 0u32);
    assert_type_eq(b, &0u32);
    assert_type_eq(c, 0u32);
}
